package com.thx.common.web;

import com.thx.common.util.AppContext;
import com.thx.common.util.StringUtil;

import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 表示一页数据.
 * 
 * @author 贾红磊
 */
public class Page<T> {
  // -- 公共变量 --//
  public static final String ASC = "asc";
  public static final String DESC = "desc";

  // -- 分页参数 --//
  protected String orderBy = null;
  protected String order = null;
  protected boolean autoCount = true;
  protected List<T> result = new ArrayList<T>();

  private int count;// 总记录数
  private int curPage = 1;// 当前页
  private int totalPage;// 总页数
  private int perPage;// 每页多少条
  private int[] pages;// 在页面显示的连接
  private int hrefCount = StringUtil.string2Int(AppContext.getProperties("hrefCountOfPage"), 8);// 最多显示的连接数

  private int goPage;// 页面提交过来的要请求的页码，一般情况goPage应该是将要提供给客户端的curPage，即goPage=curPage

  private boolean autoAdjustCurrent = true;

  /**
   * 从页面提交请求后，调用次构造来暂存传入的参数.
   * 
   * @param goPage 请求第几页
   * @param perPage 每页多少条
   */
  public Page(int goPage, int perPage) {
    this.setPerPage(perPage);
    this.setGoPage(goPage);
  }

  /**
   * 从页面提交请求后，调用次构造来暂存传入的参数.
   * 
   * @param curPage 当前页码
   * @param perPage 每页记录数
   * @param count 总记录数
   */
  public Page(int curPage, int perPage, int count) {
    // 下面三条语句的先后顺序不能更换
    this.setPerPage(perPage);// 先设置每页条数
    this.setCount(count);// 再设置总记录数
    this.setCurPage(curPage);// 最后设置当前第几页
  }

  public int getGoPage() {
    return goPage;
  }

  public void setGoPage(int goPage) {
    this.goPage = goPage;
    this.curPage = goPage;
  }

  private void setPerPage(int perPage) {
    this.perPage = perPage < 1 ? 1 : perPage;
    this.update();
  }

  /**
   * 页码.
   */
  public int[] getPages() {
    // 1 2 3 4 5 6 7 8 9 10
    int startIndex = 1;
    int endIndex = hrefCount;
    int middleIndex = hrefCount / 2;// 中间位置，本方法实现后，若是偶数条，中间位置偏左
    int offset = middleIndex - 1;// 向左偏移量 3

    startIndex = this.curPage - offset;// 假设当前页显示在中间位置，开始页的算法
    if (startIndex <= 0) {
      startIndex = 1;
    }

    endIndex = startIndex + hrefCount - 1;// 结束页算法，假设有pageCount条够显示

    if (endIndex > this.getTotalPage()) {
      // 若结束页算出后大于总页数，那开始页就得向左移动多出的页数
      startIndex -= endIndex - this.getTotalPage();
      if (startIndex <= 0) {
        startIndex = 1;
      }
      endIndex = this.getTotalPage();// 结束页面不能超过总页数
    }

    hrefCount = endIndex - startIndex + 1;// 实际页面上显示的条数

    this.pages = new int[hrefCount];
    for (int i = startIndex; i <= endIndex; i++) {
      this.pages[i - startIndex] = i;
    }

    return this.pages;
  }

  public int getCount() {
    return count;
  }

  /**
   * 设定总数.
   */
  public void setCount(int count) {
    this.count = count < 0 ? 0 : count;
    this.update();

  }

  /**
   * 设置当前页码，会自动校准最小值 0.
   */
  public void setCurPage(int current) {
    this.curPage = current;
    if (this.curPage < 0) {
      this.curPage = 0;
    }
    this.update();// 调用 update 来调整一下
  }

  public int getCurPage() {
    return curPage;
  }

  public int getTotalPage() {
    return totalPage;
  }

  public int getPerpage() {
    return perPage;
  }

  /**
   * 获取开始页码.
   */
  public int getStart() {
    if (curPage - 1 < 0) {
      curPage = 1;
    }
    return (curPage - 1) * perPage;
  }

  public int getMax() {
    return perPage;
  }

  /**
   * 获取第一页页码.
   */
  public int getFirstPage() {
    if (curPage == 1) {
      return 0;
    }
    return 1;
  }

  /**
   * 获取结束页页码.
   */
  public int getEndPage() {
    if (curPage == totalPage) {
      return 0;
    }
    return totalPage;
  }

  public int getPrevPage() {
    return curPage - 1;
  }

  /**
   * 获取下一页页码.
   */
  public int getNextPage() {
    if (curPage == totalPage || totalPage == 0) {
      return 0;
    }
    return curPage + 1;
  }

  /**
   * 通过总记录数和每页显示量来得到总共的页码<br>
   * 如果允许自动校准当前页，则当发现当前页大于最后一页的页码，便会更新当前页码为合法值<br>
   * 在设置了每页显示量、总量和自动调整后会调用本方法.
   */
  private void update() {

    this.totalPage = this.count / this.perPage;
    if (this.count % this.perPage != 0) {
      totalPage += 1;
    }

    // 自动校准
    if (this.autoAdjustCurrent) {
      if (this.curPage > this.totalPage) {
        this.setCurPage(this.totalPage);
      } else if (0 < this.totalPage && 0 >= this.curPage) {
        this.setCurPage(1);
      }
    }
  }

  /**
   * 设置是否自动校准当前页。<br/>
   * 如果自动校准则当前页超出最后一页时，自动校准为最后一页.
   * 
   * @param autoAdjustCurrent true 自动校准，false 不校准
   */
  public void setAutoAdjustCurrent(boolean autoAdjustCurrent) {
    this.autoAdjustCurrent = autoAdjustCurrent;
    this.update();// 调用 update 来调整一下
  }

  /**
   * 获得排序字段,无默认值. 多个排序字段时用','分隔.
   */
  public String getOrderBy() {
    return orderBy;
  }

  /**
   * 设置排序字段,多个排序字段时用','分隔.
   */
  public void setOrderBy(final String orderBy) {
    this.orderBy = orderBy;
  }

  /**
   * 获得排序方向, 无默认值.
   */
  public String getOrder() {
    return order;
  }

  /**
   * 设置排序方式向.
   * 
   * @param order 可选值为desc或asc,多个排序字段时用','分隔.
   */
  public void setOrder(final String order) {
    String lowcaseOrder = StringUtils.lowerCase(order);

    // 检查order字符串的合法值
    String[] orders = StringUtils.split(lowcaseOrder, ',');
    for (String orderStr : orders) {
      if (!StringUtils.equals(DESC, orderStr) && !StringUtils.equals(ASC, orderStr)) {
        throw new IllegalArgumentException("排序方向" + orderStr + "不是合法值");
      }
    }

    this.order = lowcaseOrder;
  }

  /**
   * 获得查询对象时是否先自动执行count查询获取总记录数, 默认为false.
   */
  public boolean isAutoCount() {
    return autoCount;
  }

  /**
   * 设置查询对象时是否自动先执行count查询获取总记录数.
   */
  public void setAutoCount(final boolean autoCount) {
    this.autoCount = autoCount;
  }

  /**
   * 根据curPage和perPage计算当前页第一条记录在总结果集中的位置,序号从1开始.
   */
  public int getFirst() {
    return ((curPage - 1) * perPage) + 1;
  }

  /**
   * 是否已设置排序字段,无默认值.
   */
  public boolean isOrderBySetted() {
    return (StringUtils.isNotBlank(orderBy) && StringUtils.isNotBlank(order));
  }

  /**
   * 获得页内的记录列表.
   */
  public List<T> getResult() {
    return result;
  }

  /**
   * 设置页内的记录列表.
   */
  public void setResult(final List<T> result) {
    this.result = result;
  }
}
